// 9.3 顺序容器操作
/**
 * 顺序容器和关联容器的不同之处在于两者组织元素的方式。这些不同之处直接关系到了元素如何存储、访问、添加以及删除。
 *
 * 9.3.1 向顺序容器添加元素
 * 除array外，所有标准库容器都提供灵活的内存管理。在运行时可以动态添加或删除元素来改变容器大小。
 * 表9.5列出了向顺序容器（非array）添加元素的操作。
 */

#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::cin, std::cout, std::endl;

int main()
{
    // 使用push_back
    /**
     * 除array和forward_list之外，每个顺序容器（包括string类型）都支持push_back
     * 关键概念：容器元素是拷贝.当我们用一个对象来初始化容器时，或将一个对象插入到容器中时，实际上放入到容器中的是对象值的一个拷贝，而不是对象本身。
     */
    list<string> slist1(1);
    slist1.push_back("hi!");
    string s1("hero");
    s1.push_back('s');

    // 使用push_front
    /**
     * 除了push_back，list、forward_list和deque容器还支持名为push_front的类似操作。此操作将元素插入到容器头部.
     */
    list<int> ilist1;
    for (size_t ix = 0; ix != 4; ++ix)
    {
        ilist1.push_front(ix); // 此循环将元素0、1、2、3添加到ilist1头部。
        // 在循环执行完毕后，ilist1保存序列3、2、1、0。
    }

    // 在容器中的特定位置添加元素
    /**
     * insert成员提供了更一般的添加功能，它允许我们在容器中任意位置插入0个或多个元素。vector、deque、list和string都支持insert成员。
     * forward_list提供了特殊版本的insert成员，我们将在9.3.4节（第312页）中介绍。
     * 由于迭代器可能指向容器尾部之后不存在的元素的位置，而且在容器开始位置插入元素是很有用的功能，
     * 所以insert函数将元素插入到迭代器所指定的位置之前.
     * 将元素插入到vector、deque和string中的任何位置都是合法的。然而，这样做可能很耗时。
     */
    vector<string> svec1;
    auto iter = slist1.begin();
    slist1.insert(iter, "Hello!");         // 将Hello!添加到iter之前的位置
    svec1.insert(svec1.begin(), "Hello!"); // 警告！插入到vector末尾之外的任何位置都可能很慢

    // 插入范围内元素
    /**
     * 其中一个版本接受一个元素数目和一个值，它将指定数量的元素添加到指定位置之前，这些元素都按给定值初始化。
     * 另一个版本接受一对迭代器或一个初始化列表的insert版本将给定范围中的元素插入到指定位置之前。
     */
    svec1.insert(svec1.end(), 10, "Anna"); // 将10个Anna插入到svec1末尾
    vector<string> v = {"abc", "def", "ghi"};
    slist1.insert(slist1.begin(), v.end() - 2, v.end()); // 将v的最后两个元素添加到slist1的开始位置
    slist1.insert(slist1.end(), {"xxx", "ccc"});
    slist1.insert(slist1.begin(), slist1.begin(), slist1.end()); // 运行时错误，迭代器范围不能指向与目的位置相同的容器

    // 使用emplace操作
    /**
     * 新标准引入了三个新成员——emplace_front、emplace和emplace_back，这些操作构造而不是拷贝元素。
     * 当调用push或insert成员函数时，我们将元素类型的对象传递给它们，这些对象被拷贝到容器中。
     * 而当我们调用一个emplace成员函数时，则是将参数传递给元素类型的构造函数。
     * emplace函数的参数根据元素类型而变化，参数必须与元素类型的构造函数相匹配.
     */
    list<Sales_data> c;
    // 在c的末尾构造一个Sales_data对象
    c.emplace_back("978-0590353403", 25, 15.99);          // 正确，使用三个参数的Sales_data构造函数
    c.push_back("978-0590353403", 25, 15.99);             // 错误，没有接收三个参数的push_back版本
    c.push_back(Sales_data("978-0590353403", 25, 15.99)); // 正确，使用一个临时的Sales_data对象传递给push_back
    c.emplace_back();                                     // 使用Sales_data的默认构造函数
    c.emplace(c.end(), "999-999999999");                  // 使用Sales_data(string)构造函数
    c.emplace_front("978-0590353403", 25, 15.99);         // 正确，使用三个参数的Sales_data构造函数

    return 0;
}